package com.nofeng.wx.service.appium;



import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import io.appium.java_client.AppiumBy;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.nativekey.AndroidKey;
import io.appium.java_client.android.nativekey.KeyEvent;
import io.appium.java_client.remote.options.BaseOptions;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.monitor.FileAlterationListener;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.openqa.selenium.By;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.PointerInput;
import org.openqa.selenium.interactions.Sequence;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import java.io.*;
import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;

public class AppiumService {


    public static  AndroidDriver driver;
    public static  BackgroundTaskManager threadManager;

    public static AndroidDriver getDriver() {
        if (null != driver) {
            return driver;
        }
        return null;
    }

    public static Map<String, Object> restart(String appiumServer, String appPackage, String appActivity) {
        if (null != driver) {
             driver.quit();
             driver=null;
        }
         init(appiumServer,appPackage,appActivity);
        return getDriver().getStatus();
    }
    public static BackgroundTaskManager geThreadManager() {
        if (null != threadManager) {
            return threadManager;
        }
        return null;
    }


    public static AndroidDriver init(String appiumServer, String appPackage, String appActivity) {
        if (driver == null) {
            driver = initAppiumService(defaultOptions(appPackage, appActivity), appiumServer);
        }
        if (threadManager == null) {
            threadManager =new BackgroundTaskManager();
        }
        return driver;
    }

    //"com.lphtsccft",".zhangle.main.MainActivity"
    public static BaseOptions defaultOptions(String appPackage, String appActivity) {
        return new BaseOptions()
                .amend("platformName", "Android")
                .amend("appium:automationName", "UiAutomator2")
                .amend("appium:deviceName", "Android")
                .amend("appium:ensureWebviewsHavePages", true)
                .amend("appium:nativeWebScreenshot", true)
                .amend("appium:newCommandTimeout", 3600)
                .amend("appium:connectHardwareKeyboard", true)
                .amend("fullContextList", true)
                .amend("autoLaunch", false)
                .amend("noReset", true)
                .amend("skipServerInstallation", true)
                .amend("appPackage", appPackage)
                .amend("appActivity", appActivity);
    }

    //http://8.138.24.62:4723
    public static AndroidDriver initAppiumService(BaseOptions options, String appiumServer) {
        try {
            URL url = new URL(appiumServer);
            driver = new AndroidDriver(url, options);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        return driver;
    }

    public static class StockMain {
        List<OnHandStock> onHandStockList;

        public static class OnHandStock {
            private String stockCode;
            private String stockName;

            private Integer canSellCount;

            private BigDecimal nowStockPrice;

            public String getStockCode() {
                return stockCode;
            }

            public void setStockCode(String stockCode) {
                this.stockCode = stockCode;
            }

            public String getStockName() {
                return stockName;
            }

            public void setStockName(String stockName) {
                this.stockName = stockName;
            }

            public Integer getCanSellCount() {
                return canSellCount;
            }

            public void setCanSellCount(Integer canSellCount) {
                this.canSellCount = canSellCount;
            }

            public BigDecimal getNowStockPrice() {
                return nowStockPrice;
            }

            public void setNowStockPrice(BigDecimal nowStockPrice) {
                this.nowStockPrice = nowStockPrice;
            }
        }

        public List<OnHandStock> getOnHandStockList() {
            return onHandStockList;
        }

        public void setOnHandStockList(List<OnHandStock> onHandStockList) {
            this.onHandStockList = onHandStockList;
        }
    }

    public static class AppScript {
        private String app;
        private boolean reActivateApp;

        private List<AppEvent> event;

        public static class AppEvent {
            private String action;
            private String value;

            private String xpath;

            public AppEvent(String action, String xpath) {
                this.action = action;
                this.xpath = xpath;
            }

            public String getAction() {
                return action;
            }

            public void setAction(String action) {
                this.action = action;
            }

            public String getValue() {
                return value;
            }

            public void setValue(String value) {
                this.value = value;
            }

            public String getXpath() {
                return xpath;
            }

            public void setXpath(String xpath) {
                this.xpath = xpath;
            }
        }

        public static class AppBackKeyEvent extends AppEvent {

            public AppBackKeyEvent() {
                super("backKey", null);
            }
        }

        public static class AppSetValueEvent extends AppEvent {

            public AppSetValueEvent(String xpath, String value) {
                super("setValue", xpath);
                setValue(value);
            }
        }

        public static class AppClickEvent extends AppEvent {

            public AppClickEvent(String xpath) {
                super("click", xpath);
            }

        }

        public static class AppSwipeUp extends AppEvent {

            public AppSwipeUp() {
                super("swipeUp", null);

            }
        }

        public static class AppSwipeDown extends AppEvent {

            public AppSwipeDown(String value) {
                super("swipeDown", null);
                setValue(value);
            }
        }

        public static class AppClickAndWaitEvent extends AppEvent {

            public AppClickAndWaitEvent(String xpath) {
                super("clickAndWait", xpath);
            }
        }

        public String getApp() {
            return app;
        }

        public void setApp(String app) {
            this.app = app;
        }

        public List<AppEvent> getEvent() {
            return event;
        }

        public void setEvent(List<AppEvent> event) {
            this.event = event;
        }

        public boolean isReActivateApp() {
            return reActivateApp;
        }

        public void setReActivateApp(boolean reActivateApp) {
            this.reActivateApp = reActivateApp;
        }
    }


    private void sell(String code, String price, String countDesc) {
        AppScript testScript = new AppScript();
        testScript.setApp("com.lphtsccft");
        testScript.setReActivateApp(true);
        testScript.setEvent(Lists.newArrayList());
        testScript.getEvent().add(new AppScript.AppClickAndWaitEvent("//android.widget.RadioGroup[@resource-id=\"com.lphtsccft:id/bottom_bar\"]/android.widget.RadioButton[4]"));
        testScript.getEvent().add(new AppScript.AppClickAndWaitEvent("//android.widget.TextView[@resource-id=\"com.lphtsccft:id/func_name\" and @text=\"卖\"]"));
        testScript.getEvent().add(new AppScript.AppSetValueEvent("//android.widget.EditText[@resource-id=\"com.lphtsccft:id/login_account_edit\"]", "666641539091"));
        testScript.getEvent().add(new AppScript.AppSetValueEvent("//android.widget.EditText[@resource-id=\"com.lphtsccft:id/login_password_edit\"]", "168168"));
        testScript.getEvent().add(new AppScript.AppClickAndWaitEvent("//android.widget.Button[@resource-id=\"com.lphtsccft:id/login_btn_login\"]"));
        testScript.getEvent().add(new AppScript.AppClickAndWaitEvent("//android.widget.EditText[@resource-id=\"com.lphtsccft:id/trade_editText\"]"));
        testScript.getEvent().add(new AppScript.AppSetValueEvent("//android.widget.EditText[@resource-id=\"com.lphtsccft:id/search_edit_text\"]", code));
        testScript.getEvent().add(new AppScript.AppSetValueEvent("//android.widget.EditText[@resource-id=\"com.lphtsccft:id/et_content\" and @text=\"卖出价格\"]", price));
        testScript.getEvent().add(new AppScript.AppClickAndWaitEvent("//android.widget.TextView[@resource-id=\"com.lphtsccft:id/label_name\" and @text=\"" + countDesc + "\"]"));

        testScript.getEvent().add(new AppScript.AppClickAndWaitEvent("//android.widget.TextView[@resource-id=\"com.lphtsccft:id/tv_trade\"]"));
        //testScript.getEvent().add(new AppScript.AppClickAndWaitEvent("//android.widget.TextView[@resource-id=\"com.lphtsccft:id/tv_positive\"]"));
        runAppScript(testScript);
    }

    public static  void buy(String code, String price, String count) {
        AppScript testScript = new AppScript();
        testScript.setApp("com.lphtsccft");
        testScript.setReActivateApp(true);
        testScript.setEvent(Lists.newArrayList());
        testScript.getEvent().add(new AppScript.AppClickAndWaitEvent("//android.widget.RadioGroup[@resource-id=\"com.lphtsccft:id/bottom_bar\"]/android.widget.RadioButton[4]"));
        testScript.getEvent().add(new AppScript.AppClickAndWaitEvent("//android.widget.TextView[@resource-id=\"com.lphtsccft:id/func_name\" and @text=\"买\"]"));
        testScript.getEvent().add(new AppScript.AppSetValueEvent("//android.widget.EditText[@resource-id=\"com.lphtsccft:id/login_account_edit\"]", "666641539091"));
        testScript.getEvent().add(new AppScript.AppSetValueEvent("//android.widget.EditText[@resource-id=\"com.lphtsccft:id/login_password_edit\"]", "168168"));
        testScript.getEvent().add(new AppScript.AppClickAndWaitEvent("//android.widget.Button[@resource-id=\"com.lphtsccft:id/login_btn_login\"]"));
        testScript.getEvent().add(new AppScript.AppClickAndWaitEvent("//android.widget.EditText[@resource-id=\"com.lphtsccft:id/trade_editText\"]"));
        testScript.getEvent().add(new AppScript.AppSetValueEvent("//android.widget.EditText[@resource-id=\"com.lphtsccft:id/search_edit_text\"]", code));
        testScript.getEvent().add(new AppScript.AppSetValueEvent("//android.widget.EditText[@resource-id=\"com.lphtsccft:id/et_content\" and @text=\"买入价格\"]", price));
        testScript.getEvent().add(new AppScript.AppSetValueEvent("//android.widget.EditText[@resource-id=\"com.lphtsccft:id/et_content\" and @text=\"买入数量\"]", count));
        testScript.getEvent().add(new AppScript.AppClickAndWaitEvent("//android.widget.TextView[@resource-id=\"com.lphtsccft:id/tv_trade\"]"));
        //testScript.getEvent().add(new AppScript.AppClickAndWaitEvent("//android.widget.TextView[@resource-id=\"com.lphtsccft:id/tv_positive\"]"));
    }

    private StockMain restartAndGetStockOnHand() {
        AppScript testScript = new AppScript();
        testScript.setApp("com.lphtsccft");
        testScript.setReActivateApp(true);
        testScript.setEvent(Lists.newArrayList());
        testScript.getEvent().add(new AppScript.AppClickAndWaitEvent("//android.widget.RadioGroup[@resource-id=\"com.lphtsccft:id/bottom_bar\"]/android.widget.RadioButton[4]"));
        testScript.getEvent().add(new AppScript.AppClickAndWaitEvent("//android.widget.ImageView[@resource-id=\"com.lphtsccft:id/login_image_arrow\"]"));
        testScript.getEvent().add(new AppScript.AppSetValueEvent("//android.widget.EditText[@resource-id=\"com.lphtsccft:id/login_account_edit\"]", "666641539091"));
        testScript.getEvent().add(new AppScript.AppSetValueEvent("//android.widget.EditText[@resource-id=\"com.lphtsccft:id/login_password_edit\"]", "168168"));
        testScript.getEvent().add(new AppScript.AppClickAndWaitEvent("//android.widget.Button[@resource-id=\"com.lphtsccft:id/login_btn_login\"]"));
        testScript.getEvent().add(new AppScript.AppSwipeUp());
        testScript.getEvent().add(new AppScript.AppSwipeUp());
        testScript.getEvent().add(new AppScript.AppSwipeUp());
        runAppScript(testScript);
        String pageSource = driver.getPageSource();
        NodeList stockNodeList = xml(pageSource, "//android.widget.TextView[@resource-id=\"com.lphtsccft:id/stock_name\"]");
        NodeList stockNowPrieList = xml(pageSource, "//android.widget.TextView[@resource-id=\"com.lphtsccft:id/item_jryk\"]");
        NodeList canOptionCountList = xml(pageSource, "//android.widget.TextView[@resource-id=\"com.lphtsccft:id/item_cb\"]");
        StockMain stockMain = new StockMain();
        stockMain.setOnHandStockList(Lists.newArrayList());
        if (stockNodeList.getLength() > 0) {
            for (int i = 0; i < stockNodeList.getLength(); i++) {
                Node item = stockNodeList.item(i);
                Node nowPrice = stockNowPrieList.item(i);
                Node canOptionCount = canOptionCountList.item(i);
                StockMain.OnHandStock onHandStock = new StockMain.OnHandStock();
                stockMain.getOnHandStockList().add(onHandStock);
                onHandStock.setStockName(item.getAttributes().getNamedItem("text").getNodeValue());
                onHandStock.setNowStockPrice(new BigDecimal(nowPrice.getAttributes().getNamedItem("text").getNodeValue().replace(",", "")));
                onHandStock.setCanSellCount(new Integer(canOptionCount.getAttributes().getNamedItem("text").getNodeValue().replace(",", "")));
            }
        }
        return stockMain;
    }

    public static  void monitorFile(Consumer<AppScript> callback) {
        File directoryToMonitor = new File("d:/appscript");

        // 创建一个监听器
        FileAlterationListener listener = new FileAlterationListenerAdaptor() {
            @Override
            public void onFileCreate(File file) {
                System.out.println("Created: " + file);
            }

            @Override
            public void onFileDelete(File file) {
                System.out.println("Deleted: " + file);
            }

            @Override
            public void onFileChange(File file) {
                System.out.println("Changed: " + file);
                if (file.getName().equals("script.txt")) {
                    InputStream is = null;
                    try {
                        is = new FileInputStream(file);
                    } catch (FileNotFoundException e) {
                        throw new RuntimeException(e);
                    }
                    List<String> data = IOUtils.readLines(is, "utf-8");
                    String result = "";
                    for (String datum : data) {
                        result += datum;
                    }
                    AppScript appScript = JSONObject.parseObject(result, AppScript.class);
                    callback.accept(appScript);
                }
            }
        };

        // 创建观察者
        FileAlterationObserver observer = new FileAlterationObserver(directoryToMonitor);
        observer.addListener(listener);

        try {
            // 创建监控器，设置检查间隔为5000毫秒（5秒）
            FileAlterationMonitor monitor = new FileAlterationMonitor(5000);
            monitor.addObserver(observer);

            // 开始监控
            monitor.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static class BackgroundTaskManager{
        private ExecutorService executorService;
        private  AtomicBoolean running;
        private final AtomicLong startTime;

        public BackgroundTaskManager() {
            // 使用单线程的线程池来执行任务
            executorService = Executors.newSingleThreadExecutor();
            running = new AtomicBoolean(true);
            startTime = new AtomicLong(System.currentTimeMillis());
        }

        // 设置最大运行时间（毫秒）
        public void setMaxRuntime(long maxRuntime) {
            startTime.set(System.currentTimeMillis() + maxRuntime);
        }

        // 提交任务到后台执行
        public void submitTask(Runnable task,long maxRuntime) {
            long currentTime = System.currentTimeMillis();
            running.set(true);
            setMaxRuntime(maxRuntime);
            if (running.get()) {
                if( currentTime< startTime.get()|| maxRuntime<=0) {
                    executorService.submit(() -> {
                        while (System.currentTimeMillis() < startTime.get()) {
                            task.run();
                        }
                    });
                }
            }
        }
        public void submitTask(Runnable task) {
            if (running.get()) {
                executorService.submit(task);
            }
        }


        // 关闭所有线程，等待已提交的任务完成后再关闭
        public void shutdownNow(Consumer<AndroidDriver> c) {
            c.accept(getDriver());
            executorService.shutdownNow();
            // 使用单线程的线程池来执行任务
            executorService = Executors.newSingleThreadExecutor();
            running.set(false);
        }
    }

    public static void runAppScript(AppScript appScript) {
        geThreadManager().shutdownNow(driver->{});
        geThreadManager().submitTask(new Runnable() {
            @Override
            public void run() {
                String appPackage = appScript.getApp();
                if (appScript.isReActivateApp()) {
                    driver.terminateApp(appPackage);
                    driver.activateApp(appPackage);
                }
                for (AppScript.AppEvent appEvent : appScript.getEvent()) {
                    String action = appEvent.getAction();
                    String xpath = appEvent.getXpath();
                    if (action.equals("clickAndWait")) {
                        clickXpathForBackGround(xpath);
                    }
                    if (action.equals("click")) {
                        clickXpath(xpath);
                    }
                    if (action.equals("setValue")) {
                        setValueXpathForBackGround(xpath, appEvent.getValue());
                    }
                    if (action.equals("swipeUp")) {
                        swipeUp();
                    }
                    if (action.equals("swipeDown")) {
                        swipeDown();
                    }
                    if (action.equals("backKey")) {
                        driver.pressKey(new KeyEvent(AndroidKey.BACK));
                    }
                }
            }
        });

    }

    public static  void swipeUp() {
        Dimension size = driver.manage().window().getSize();
        int width = size.width;
        int height = size.height;
        int targetWidth = width / 2;
        int oriHeight = height * 3 / 4;
        int targetHeight = height / 4;
        swipe(targetWidth, targetWidth, oriHeight, targetHeight);
    }

    public static  void swipeDown() {
        Dimension size = driver.manage().window().getSize();
        int width = size.width;
        int height = size.height;

        int targetWidth = width / 2;
        int oriHeight = height / 4;
        int targetHeight = height * 3 / 4;
        swipe(targetWidth, targetWidth, oriHeight, targetHeight);
    }

    public static  void swipe(int x, int tx, int y, int ty) {
        try {
            // 创建指针输入源
            PointerInput touchPointer = new PointerInput(PointerInput.Kind.TOUCH, "finger");

            // 创建动作序列
            Sequence scrollUp = new Sequence(touchPointer, 0);
            scrollUp.addAction(touchPointer.createPointerMove(Duration.ofMillis(0), PointerInput.Origin.viewport(), x, y));
            scrollUp.addAction(touchPointer.createPointerDown(PointerInput.MouseButton.LEFT.asArg()));
            scrollUp.addAction(touchPointer.createPointerMove(Duration.ofMillis(700), PointerInput.Origin.viewport(), tx, ty));
            scrollUp.addAction(touchPointer.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));

            // 执行动作
            driver.perform(Collections.singletonList(scrollUp));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private  static void clickXpath(String xpath) {
        if (isExistByPath(xpath)) {
            driver.findElement(AppiumBy.xpath(xpath)).click();
        }
    }

    private void clickById(String selector) {
        try {
            driver.findElement(AppiumBy.id(selector)).click();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void clickXpathForBackGround(String xpath) {
        clickXpathForBackGround(xpath, 60);
    }

    public static  void clickXpathForBackGround(String xpath, Integer sec) {
        eventForBackGround(AppiumBy.xpath(xpath), sec, e -> {
            e.click();
        });
    }

    public static  void setValueXpathForBackGround(String xpath, String value) {
        setValueXpathForBackGround(xpath, value, 60);
    }

    public static  void setValueXpathForBackGround(String xpath, String value, Integer sec) {
        eventForBackGround(AppiumBy.xpath(xpath), sec, e -> {
            e.sendKeys(new CharSequence[]{value});
        });
    }

    public static  void eventForBackGround(By selector, Integer sec, Consumer<WebElement> callback) {
        try {
            WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(sec));
            WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(selector));
            callback.accept(element);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private WebElement byId(String selector) {
        try {
            return driver.findElement(AppiumBy.id(selector));
        } catch (NoSuchElementException e) {
            return null;
        }
    }

    public static  boolean isExistByPath(String xpath) {
        try {
            driver.findElement(AppiumBy.xpath(xpath));
            return true;
        } catch (NoSuchElementException e) {
            return false;
        }
    }

    public NodeList xml(String xml, String xpath) {
        try {
            // 创建DocumentBuilderFactory实例
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            // 创建DocumentBuilder实例
            DocumentBuilder builder = factory.newDocumentBuilder();
            // 解析XML文件，获取Document实例
            Document document = builder.parse(new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)));

            // 创建XPathFactory实例
            XPathFactory xpathFactory = XPathFactory.newInstance();
            // 通过XPathFactory实例化XPath对象
            XPath xpathEx = xpathFactory.newXPath();

            // 编译XPath表达式
            // 例如，获取所有具有id属性的book节点
            XPathExpression expr = xpathEx.compile(xpath);
            // 执行XPath表达式，获取结果
            NodeList nodes = (NodeList) expr.evaluate(document, XPathConstants.NODESET);
            return nodes;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

    public void tearDown() {
        driver.quit();
    }

}
